#include"CommonConnectionPool.hpp"
#include"db.h"

// 单例模式,获取连接池实例
ConnectionPool* ConnectionPool::getConnectionPool() 
{
	static ConnectionPool pool;
	return &pool;
}

// 加载配置文件中的数据
bool ConnectionPool::loadConfigFile()
{
	FILE* fp = fopen("mysql.ini", "r");
	if (fp == nullptr) {
		LOG_INFO << __FILE__ << ":" << __LINE__ << ":"  << "mysql.ini file is not exist!";
		return false;
	}
	while (!feof(fp)) {
		char line[1024] = { 0 };
		fgets(line, 1024, fp);
		string str = line;
		int idx = str.find('=', 0);
		if (idx == -1) continue;//无效配置项
		int end = str.find('\n', idx);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, end - idx - 1);
		
		if (key == "ip") _ip = value;
		else if (key == "port") _port = atoi(value.c_str());
		else if (key == "username") _username = value;
		else if (key == "password") _password = value;
		else if (key == "dbname") _dbname = value;
		else if (key == "initSize") _initSize = atoi(value.c_str());
		else if (key == "maxSize") _maxSize = atoi(value.c_str());
		else if (key == "maxIdleTime") _maxIdleTime = atoi(value.c_str());
		else if (key == "connectionTimeout") _connectionTimeout = atoi(value.c_str());
	}
	return true;
}

//构造函数初始化
ConnectionPool::ConnectionPool()
{
    // 加载配置项
    if(!loadConfigFile())
    {
        return;
    }

    //创建初始数量的链接
    for(int i=0; i < _initSize; ++i)
    {
        MySQL *p = new MySQL();
        p->connect(); //数据库链接
		p->refreshAliveTime(); //刷新一下链接的空闲时间

        _connectionQue.push(p);
        _connectionCnt++;
    }

    //创建一个 链接生产者线程,利用绑定器 --》 绑定this指针
    thread produce(std::bind(&ConnectionPool::produceConnection, this));
	produce.detach();

	//启动一个新的定时线程，扫描超过 超时空闲时间 的链接，并且回收
	thread scanner(std::bind(&ConnectionPool::scannerConnectionTask, this));
	scanner.detach();
}

// 负责生产新连接的 线程方法
void ConnectionPool::produceConnection()
{
    for(;;)
    {
		unique_lock<mutex> lock(_queueMutex);

        while (!_connectionQue.empty())
        {
            cv.wait(lock); //队列不空， 等待
        }
        
		//小于最大链接量 才创建 链接
		if(_connectionCnt < _maxSize) 
		{
			MySQL *p = new MySQL();
			p->connect(); //数据库链接
			p->refreshAliveTime(); //刷新一下链接的空闲时间

			_connectionQue.push(p);
			_connectionCnt++;			
		}

		//通知消费者，可以 消费链接了
		cv.notify_one();
    }
}

//负责扫描超过 超时空闲时间 的链接，并且回收
void ConnectionPool::scannerConnectionTask()
{
	for(;;)
	{
		//通过sleep 模拟定时效果
		this_thread::sleep_for(chrono::seconds(_maxIdleTime)); 

		//扫描整个队列，释放 多余链接
		unique_lock<mutex> lock(_queueMutex);

		while (_connectionCnt > _initSize)
		{
			MySQL *p = _connectionQue.front();
			if(p->getAliveTime() >= (_maxIdleTime*1000) ) 
			{
				_connectionQue.pop();
				_connectionCnt--;
				delete p; //释放链接
			}
			else
				break;	//对头链接没有超过，后面肯定不会超过了
		}
		
	}
}

//提供给 外部，从 链接池里获取一个可用的 链接
shared_ptr<MySQL>  ConnectionPool::getConnection()
{
	unique_lock<mutex> lock(_queueMutex);
	while (_connectionQue.empty())
	{
		//假如为空，等待 超时时间 
		if(cv_status::timeout == cv.wait_for(lock,chrono::microseconds(_connectionTimeout)))
		{
			if(_connectionQue.empty()) //还是空的
			{
				LOG_INFO << "获取空闲链接超时了。。";
				return nullptr;
			}
		}
	}

	//队列不为空
	shared_ptr<MySQL> sp(_connectionQue.front(),
	 [&](MySQL *pcon) { //定制删除器，要在服务器应用线程中使用，所以要考虑线程安全的问题 
		unique_lock<mutex> lock(_queueMutex);
		pcon->refreshAliveTime();
		_connectionQue.push(pcon);
	 });
	_connectionQue.pop();

	if(_connectionQue.empty())
	cv.notify_one(); //队列为空了，通知生产者 生产链接了

	return sp;
}

